<template>
  <SysDialog
    ref="dialogRef"
    @onSubmit="onSubmit"
    @onClose="onClose"
    :open-delay=1
  >
    <template #content>
      <el-tree
        class="tree"
        ref="treeRef"
        :data="treeDataList"
        show-checkbox
        node-key="value"
        check-strictly
        default-expand-all
        icon="document"
        @check="handleCheck"
      />
    </template>
  </SysDialog>
</template>

<script lang="ts" setup>


import { editRolePermissionInfo, getPermissionTreeList } from '@/api/usermgr'
import { FormMode, PermissionType } from '@/common/type/baseEnum'
import type { RoleData } from '@/common/type/data'
import SysDialog from '@/components/SysDialog.vue'
import { isResultSuccess } from '@/util/http'
import Logger from '@/util/log'
import { ElMessage, ElTree } from 'element-plus'
import { nextTick, onMounted, reactive, ref, watch } from 'vue'

const props = defineProps({

})

const treeDataList = reactive([])
const rawData = ref<RoleData>({} as RoleData)

const dialogRef = ref()
const treeRef = ref()
// note：dialogRef和treeRef打开的时机不同，导致rawData.permissionIDs的使用不同
// 同时使用:open-delay=1来延迟打开dialog，避免setCheckedKeys有残影！前端的骚操作！！！
const open = (m: FormMode, data: RoleData) => {
  dialogRef.value.open(m) // 先打开dialog再渲染tree
  if (data) {
    Object.assign(rawData.value, data)
    nextTick(() => {
      treeRef.value.setCheckedKeys(rawData.value.permissionIDs)
      // Logger.prettyInfo('open form data', rawData.value.permissionIDs)
    })
  }
}

const onSubmit = async () => {
  let ids: number[] = []
  treeRef.value.getCheckedNodes().forEach((node: any) => {
    ids.push(node.value)
  })
  Logger.info('submit start!', treeRef.value.getCheckedNodes(), ids, rawData.value)

  try {
    rawData.value.permissionIDs = ids
    const res = await editRolePermissionInfo(rawData.value)
    if (res && isResultSuccess(res)) {
      ElMessage.success('分配权限成功')
      emit('success')
    }
  } catch (error) {
    Logger.prettyError('editRoleInfo catch error', error)
    ElMessage.error('分配权限失败')
  }

  dialogRef.value.close()
}

const onClose = () => {
  // Logger.info('close dialog', rawData.value)
}

defineExpose({
  open,
})

const emit = defineEmits(['success'])

const refreshPermissionTreeList = async () => {
  const res = await getPermissionTreeList({
    excludeOperation: false,
  })
  if (res && isResultSuccess(res)) {
    Object.assign(treeDataList, res.data)
    // Logger.prettyInfo('getPermissionTreeList', treeDataList)
  }
}

onMounted(() => {
  // Logger.info('RolePermissionDialog mounted!')
  refreshPermissionTreeList()
})

// check-strictly时一些联动处理逻辑，比较复杂
// 原因就是因为如果某菜单只是想查看，而不需要操作，不check-strictly就无法实现
// 当然也可以考虑每个菜单下面新增一个查看权限，这是另一种做法
const handleCheck = (checkedNode: any) => {
  let checkedKeys = treeRef.value.getCheckedKeys()

  // 取消选择逻辑
  if (!checkedKeys.includes(checkedNode.value)) {
    const collectAllKeys = (node: any): any[] => {
      let keys = [node.value]
      if (node.children?.length) {
        keys = keys.concat(node.children.flatMap(collectAllKeys))
      }
      return keys
    }
    const removedKeys = collectAllKeys(checkedNode)
    checkedKeys = checkedKeys.filter((k: any) => !removedKeys.includes(k))
    // Logger.info('deselect checkedNode', checkedNode, removedKeys, checkedKeys)
  }

  // 选中处理逻辑
  else {
    if (checkedNode.type === PermissionType.Directory) {
      const collectMenuKeys = (node: any): any[] => {
        let keys = []
        if (node.type === PermissionType.Menu) keys.push(node.value)
        if (node.type === PermissionType.Directory && node.children?.length) {
          keys.push(node.value)
          keys = keys.concat(node.children.flatMap(collectMenuKeys))
        }
        return keys
      }
      const childKeys = checkedNode.children.flatMap(collectMenuKeys)
      checkedKeys = checkedKeys.concat(childKeys)
      // Logger.info('select checkedNode', checkedNode, childKeys, checkedKeys)
    }
  }
  treeRef.value.setCheckedKeys(checkedKeys)
  syncParentState(checkedNode)

  // 更新原始数据
  rawData.value.permissionIDs = treeRef.value.getCheckedKeys()
}
// 如果某节点被选中或者取消选中时，检测其同级节点是否全部被选中或者取消选中，
// 若全部被选中则父节点也被选中，若全部取消选中则父节点也取消选中
const syncParentState = (node: any) => {
  const parentNode = treeRef.value.getNode(node).parent
  if (!parentNode || parentNode.level === 0) return

  const siblings = parentNode.childNodes
  const allChecked = siblings.every((s: any) => treeRef.value.getCheckedKeys().includes(s.key))
  const noneChecked = siblings.every((s: any) => !treeRef.value.getCheckedKeys().includes(s.key))

  if (allChecked) {
    treeRef.value.setChecked(parentNode.key, true, false)
  } else if (noneChecked) {
    treeRef.value.setChecked(parentNode.key, false, false)
  }

  syncParentState(parentNode)
};

</script>

<style scoped>
.tree {
  width: 50%;
  margin: 0 auto;
}
</style>